{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = 'all'\n",
    "\n",
    "%load_ext autoreload\n",
    "%autoreload 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "sys.path.append('/Users/siyuyang/Source/repos/GitHub_MSFT/CameraTraps-api')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import io\n",
    "import os\n",
    "import json\n",
    "\n",
    "import numpy as np\n",
    "import requests\n",
    "from requests_toolbelt.multipart import decoder\n",
    "from PIL import Image\n",
    "\n",
    "from visualization.visualization_utils import draw_bounding_boxes_on_image, draw_bounding_box_on_image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'http://:/v1/camera-trap/sync/'"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'http://boto.eastus.cloudapp.azure.com:6002/v1/camera-trap/sync/'"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# this example notebook will process the first 8 images with filenames ending in .jpg or .JPG in this directory\n",
    "sample_input_dir = '../sample_input/test_images'\n",
    "\n",
    "ip_address = ''\n",
    "port = ''\n",
    "base_url = 'http://{}:{}/v1/camera-trap/sync/'.format(ip_address, port)  # insert the IP address of the API\n",
    "base_url\n",
    "\n",
    "# override with production URL\n",
    "base_url = 'http://boto.eastus.cloudapp.azure.com:6002/v1/camera-trap/sync/'\n",
    "base_url"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# copy API key if testing production endpoint\n",
    "API_KEY = ''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Find out the version of the camera trap model used\n",
    "Good endpoint to check that it is alive"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'v4.1.0'"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# re-using the headers field messes with the requests connection...\n",
    "headers = {\n",
    "    'Ocp-Apim-Subscription-Key': API_KEY\n",
    "}\n",
    "\n",
    "version_info = requests.get(base_url + 'detector_model_version', headers=headers)\n",
    "version_info.text"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Calling the API"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "number of images to send: 8\n",
      "CPU times: user 618 µs, sys: 1.18 ms, total: 1.8 ms\n",
      "Wall time: 2.16 ms\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "# re-using the headers field messes with the requests connection...\n",
    "headers = {\n",
    "    'Ocp-Apim-Subscription-Key': API_KEY\n",
    "}\n",
    "\n",
    "num_images_to_upload = 8  # the /detect endpoint currently accepts a maximum of 8 images\n",
    "\n",
    "detection_confidence = 0.8  # a value from 0 to 1\n",
    "render_boxes = True  # True to have returned result contain annotated image files; in any case a json with result will be returned\n",
    "\n",
    "params = {\n",
    "    'confidence': detection_confidence,\n",
    "    'render': render_boxes\n",
    "}\n",
    "\n",
    "files = {}\n",
    "\n",
    "num_images = 0\n",
    "open_files = []\n",
    "images_processed = {}\n",
    "for i, image_name in enumerate(sorted(os.listdir(sample_input_dir))):\n",
    "    if not image_name.lower().endswith('.jpg'):\n",
    "        continue\n",
    "    \n",
    "    if num_images >= num_images_to_upload:\n",
    "        break\n",
    "    else:\n",
    "        num_images += 1\n",
    "    \n",
    "    img_path = os.path.join(sample_input_dir, image_name)\n",
    "    fd = open(img_path, 'rb')\n",
    "    open_files.append(fd)\n",
    "    files[image_name] = (image_name, fd, 'image/jpeg')\n",
    "    images_processed[image_name] = img_path  # check bbox rendering\n",
    "\n",
    "print('number of images to send:', len(files))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "response:  200\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "8.286973"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "r = requests.post(base_url + 'detect', \n",
    "                  params=params,\n",
    "                  files=files, headers=headers)\n",
    "print('response: ', r.status_code)\n",
    "\n",
    "if not r.ok:\n",
    "    r.reason\n",
    "    r.text\n",
    "r.elapsed.total_seconds()\n",
    "\n",
    "for fd in open_files:\n",
    "    fd.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'Server': 'gunicorn/20.0.4', 'Date': 'Wed, 22 Jul 2020 21:18:45 GMT', 'Connection': 'close', 'Content-Type': 'multipart/form-data; boundary=3bc2e5933dfe44ce8376378cf6c64cb5', 'Content-Length': '5110729'}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "r.headers"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## Reading the returned result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "res = decoder.MultipartDecoder.from_response(r)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "results = {}\n",
    "images = {}\n",
    "\n",
    "for part in res.parts:\n",
    "    # part is a BodyPart object with b'Content-Type', and b'Content-Disposition', the later includes 'name' and 'filename' info\n",
    "    headers = {}\n",
    "    for k, v in part.headers.items():\n",
    "        headers[k.decode(part.encoding)] = v.decode(part.encoding)\n",
    "\n",
    "    if headers.get('Content-Type', None) == 'image/jpeg':\n",
    "        #images[part.headers['filename']] = part.content\n",
    "        c = headers.get('Content-Disposition')\n",
    "        image_name = c.split('name=\"')[1].split('\"')[0]  # somehow all the filename and name info is all in one string with no obvious forma\n",
    "        image = Image.open(io.BytesIO(part.content))\n",
    "        images[image_name] = image\n",
    "    elif headers.get('Content-Type', None) == 'application/json':\n",
    "        content_disposition = headers.get('Content-Disposition', '')\n",
    "        if 'detection_result' in content_disposition:\n",
    "            results['detection_result'] = json.loads(part.content.decode())\n",
    "        elif 'classification_result' in content_disposition:\n",
    "            results['classification_result'] = json.loads(part.content.decode())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'detection_result': {'S1_D04_R6_PICT0020.JPG': [[0,\n",
       "    0.006578,\n",
       "    0.9401,\n",
       "    0.9797779999999999,\n",
       "    0.998,\n",
       "    1]],\n",
       "  'S1_D04_R6_PICT0021.JPG': [[0, 0.01237, 0.9344, 0.93057, 0.998, 1]],\n",
       "  'S1_D04_R6_PICT0022.JPG': [[0, 0, 0.9251, 0.9905, 0.999, 1]],\n",
       "  'S1_D04_R6_PICT0128.JPG': [[0.5913, 0.0171, 0.67462, 0.1704, 0.997, 1]],\n",
       "  'S1_D04_R6_PICT0129.JPG': [],\n",
       "  'S1_D04_R6_PICT0130.JPG': [],\n",
       "  'S1_D04_R6_PICT0221.JPG': [[0.5308,\n",
       "    0.9349,\n",
       "    0.62034,\n",
       "    0.9992099999999999,\n",
       "    0.988,\n",
       "    1]],\n",
       "  'S1_D04_R6_PICT0222.JPG': [[0.5113, 0.8766, 0.6167, 0.9989, 0.999, 1]]},\n",
       " 'classification_result': {}}"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "for img_name, img in sorted(images.items()):\n",
    "    print(img_name)\n",
    "    img\n",
    "    img.close()  # close after displaying\n",
    "    print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "# TO FIX draw_bounding_boxes_on_image() does not modify in-place...?\n",
    "\n",
    "for image_name, image_path in images_processed.items():\n",
    "    image = Image.open(image_path)\n",
    "    detections = results['detection_result'][image_name]\n",
    "    \n",
    "    boxes, classes = [], []\n",
    "    for i in detections:\n",
    "        boxes.append(i[:4])\n",
    "        classes.append(i[5])\n",
    "    \n",
    "    for box in boxes:\n",
    "        draw_bounding_box_on_image(image,\n",
    "                               box[0],\n",
    "                               box[1],\n",
    "                               box[2],\n",
    "                               box[3],\n",
    "                               clss=1,\n",
    "                               thickness=4,\n",
    "                               expansion=0,\n",
    "                               display_str_list=(),\n",
    "                               use_normalized_coordinates=True,\n",
    "                               label_font_size=16)\n",
    "        image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python [conda env:cameratraps] *",
   "language": "python",
   "name": "conda-env-cameratraps-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
